import 'dart:ui';

import 'package:flutter/material.dart';

import '../common/coordinate.dart';

class PaperBezier3 extends StatefulWidget {
  const PaperBezier3({super.key});

  @override
  State<PaperBezier3> createState() => _PaperState();
}

class _PaperState extends State<PaperBezier3> {
  final TouchInfo touchInfo = TouchInfo();

  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    touchInfo.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
        onPanDown: _onPanDown,
        onPanUpdate: _onPanUpdate,
        onPanEnd: _onPanEnd,
        child: Container(
          color: Colors.white,
          child: CustomPaint(
            painter: PaperPainter(repaint: touchInfo),
          ),
        ));
  }

  void _onPanDown(DragDownDetails details) {
    if (touchInfo.points.length < 4) {
      touchInfo.addPoint(details.localPosition);
    } else {
      judgeZone(details.localPosition);
    }
  }

  void _onPanUpdate(DragUpdateDetails details) {
    judgeZone(details.localPosition, update: true);
  }

  void _onPanEnd(DragEndDetails details) {}

  /// 判断是否在某点等半径r圆等范围内
  bool judgeCircleArea(Offset src, Offset dst, double r) =>
      (src - dst).distance <= r;

  /// 判断哪个点被选中
  void judgeZone(Offset src, {bool update = false}) {
    for (int i = 0; i < touchInfo.points.length; i++) {
      if (judgeCircleArea(src, touchInfo.points[i], 15)) {
        touchInfo.selectIndex = i;
        if (update) {
          touchInfo.updatePoint(i, src);
        }
      }
    }
  }
}

class PaperPainter extends CustomPainter {
  final TouchInfo repaint;

  PaperPainter({required this.repaint}) : super(repaint: repaint);

  List<Offset> pos = [];

  final Paint mPaint = Paint()
    ..style = PaintingStyle.stroke
    ..strokeWidth = 2
    ..color = Colors.orange;
  var helpPaint = Paint()
    ..color = Colors.purple
    ..style = PaintingStyle.stroke
    ..strokeCap = StrokeCap.round;
  final Path mPath = Path();
  final CoordinateByPath coordinate = CoordinateByPath();

  @override
  void paint(Canvas canvas, Size size) {
    coordinate.paint(canvas, size);
    // 原点坐标移动到屏幕中心
    canvas.translate(size.width / 2, size.height / 2);

    testDrawBezier(canvas, size);
  }

  /// 三次贝塞尔曲线
  void testDrawBezier(Canvas canvas, Size size) {
    pos = repaint.points
        .map((e) => e.translate(-size.width / 2, -size.height / 2))
        .toList();
    if (pos.length < 4) {
      canvas.drawPoints(PointMode.points, pos, helpPaint..strokeWidth = 8);
    } else {
      mPath.reset();
      mPath.moveTo(pos[0].dx, pos[0].dy);
      mPath.cubicTo(
          pos[1].dx, pos[1].dy, pos[2].dx, pos[2].dy, pos[3].dx, pos[3].dy);
      canvas.drawPath(mPath, mPaint);
      drawHelp(canvas, pos);
      drawSelectPos(canvas, size);
    }
  }

  void drawHelp(Canvas canvas, List<Offset> pos) {
    //canvas.drawPoints(PointMode.lines, [Offset.zero, p1, p1, p2], helpPaint..strokeWidth = 2);
    //canvas.drawPoints(PointMode.points, [Offset.zero, p1, p1, p2], helpPaint..strokeWidth = 8);
    helpPaint.color = Colors.purple;
    canvas.drawPoints(PointMode.polygon, pos, helpPaint..strokeWidth = 2);
    canvas.drawPoints(PointMode.points, pos, helpPaint..strokeWidth = 8);
  }

  void drawSelectPos(Canvas canvas, Size size) {
    Offset? selectPos = repaint.selectPoint;
    if (selectPos != null) {
      selectPos = selectPos.translate(-size.width / 2, -size.height / 2);
      canvas.drawCircle(
          selectPos,
          10,
          helpPaint
            ..color = Colors.green
            ..strokeWidth = 2);
    }
  }

  @override
  bool shouldRepaint(covariant PaperPainter oldDelegate) =>
      oldDelegate.repaint != repaint;
}

class TouchInfo extends ChangeNotifier {
  final List<Offset> _points = [];
  int _selectIndex = -1;

  int get selectIndex => _selectIndex;

  List<Offset> get points => _points;

  set selectIndex(int value) {
    if (_selectIndex == value) return;
    _selectIndex = value;
    notifyListeners();
  }

  void addPoint(Offset point) {
    _points.add(point);
    notifyListeners();
  }

  void updatePoint(int index, Offset point) {
    _points[index] = point;
    notifyListeners();
  }

  Offset? get selectPoint => _selectIndex == -1 ? null : _points[_selectIndex];
}
